using System.Collections.Generic;

public class ThreadSafeDoubleQueue<T>
{
    private class DataBuffer
    {
        private T[] m_buffer;
        private int m_rpos;
        private int m_wpos;

        public DataBuffer(int size)
        {
            m_buffer = new T[size];
        }

        public bool IsEmpty()
        {
            return m_rpos >= m_wpos;
        }

        public bool IsFull()
        {
            return m_wpos >= m_buffer.Length;
        }

        public T Get()
        {
            return m_buffer[m_rpos++];
        }

        public void Put(T v)
        {
            m_buffer[m_wpos++] = v;
        }

        public void Reset()
        {
            m_rpos = m_wpos = 0;
        }
    }

    private DataBuffer m_outBuffer;
    private DataBuffer m_inBuffer;
    private Queue<DataBuffer> m_inBuffers;
    private int m_bufferSize;

    public ThreadSafeDoubleQueue(int size = 256)
    {
        m_bufferSize = size;
    }

    public void Enqueue(T v)
    {
        lock (this)
        {
            if (m_inBuffer != null && m_inBuffer.IsFull())
            {
                if (m_inBuffers == null)
                {
                    m_inBuffers = new Queue<DataBuffer>(1);
                }
                m_inBuffers.Enqueue(m_inBuffer);
                m_inBuffer = null;
            }
            if (m_inBuffer == null)
            {
                m_inBuffer = new DataBuffer(m_bufferSize);
            }
            m_inBuffer.Put(v);
        }
    }

    public bool Dequeue(out T v)
    {
        if (m_outBuffer != null && !m_outBuffer.IsEmpty())
        {
            v = m_outBuffer.Get();
            return true;
        }
        lock (this)
        {
            if (m_inBuffers == null || m_inBuffers.Count == 0)
            {
                Utils.Swap(ref m_inBuffer, ref m_outBuffer);
                if (m_inBuffer != null)
                {
                    m_inBuffer.Reset();
                }
            }
            else
            {
                m_outBuffer = m_inBuffers.Dequeue();
            }
        }
        if (m_outBuffer != null && !m_outBuffer.IsEmpty())
        {
            v = m_outBuffer.Get();
            return true;
        }
        v = default(T);
        return false;
    }

    public void Clear()
    {
        lock (this)
        {
            if (m_outBuffer != null)
            {
                m_outBuffer.Reset();
            }
            if (m_inBuffer != null)
            {
                m_inBuffer.Reset();
            }
            m_inBuffers.Clear();
        }
    }
}
